{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "############## PLEASE RUN THIS CELL FIRST! ###################\n",
    "\n",
    "# import everything and define a test runner function\n",
    "from importlib import reload\n",
    "from helper import run\n",
    "import bloomfilter\n",
    "import block\n",
    "import ecc\n",
    "import helper\n",
    "import merkleblock\n",
    "import network\n",
    "import script\n",
    "import tx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from helper import hash256\n",
    "bit_field_size = 10\n",
    "bit_field = [0] * bit_field_size\n",
    "h = hash256(b'hello world')\n",
    "bit = int.from_bytes(h, 'big') % bit_field_size\n",
    "bit_field[bit] = 1\n",
    "print(bit_field)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from helper import hash256\n",
    "bit_field_size = 10\n",
    "bit_field = [0] * bit_field_size\n",
    "for item in (b'hello world', b'goodbye'):\n",
    "    h = hash256(item)\n",
    "    bit = int.from_bytes(h, 'big') % bit_field_size\n",
    "    bit_field[bit] = 1\n",
    "print(bit_field)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 1\n",
    "\n",
    "Calculate the Bloom Filter for 'hello world' and 'goodbye' using the `hash160` hash function over a bit field of 10."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 1\n",
    "\n",
    "from helper import hash160\n",
    "\n",
    "bit_field_size = 10\n",
    "bit_field = [0] * bit_field_size\n",
    "items = (b'hello world', b'goodbye')\n",
    "# loop through each item\n",
    "    # hash160 the item\n",
    "    # interpret hash as a Big-Endian integer and mod by bit_field_size\n",
    "    # set that bit in bit_field to 1\n",
    "# print the bit_field"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from helper import hash256, hash160\n",
    "bit_field_size = 10\n",
    "bit_field = [0] * bit_field_size\n",
    "for item in (b'hello world', b'goodbye'):\n",
    "    for hash_function in (hash256, hash160):\n",
    "        h = hash_function(item)\n",
    "        bit = int.from_bytes(h, 'big') % bit_field_size\n",
    "        bit_field[bit] = 1\n",
    "print(bit_field)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from helper import murmur3\n",
    "from bloomfilter import BIP37_CONSTANT\n",
    "field_size = 2\n",
    "num_functions = 2\n",
    "tweak = 42\n",
    "bit_field_size = field_size * 8\n",
    "bit_field = [0] * bit_field_size\n",
    "for phrase in (b'hello world', b'goodbye'):\n",
    "    for i in range(num_functions):\n",
    "        seed = i * BIP37_CONSTANT + tweak\n",
    "        h = murmur3(phrase, seed=seed)\n",
    "        bit = h % bit_field_size\n",
    "        bit_field[bit] = 1\n",
    "print(bit_field)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 2\n",
    "\n",
    "Given a Bloom Filter with size=10, function count=5, tweak=99, what are the bytes that are set after adding these items? (use `bit_field_to_bytes` to convert to bytes)\n",
    "\n",
    "* `b'Hello World'`\n",
    "* `b'Goodbye!'`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 2\n",
    "\n",
    "from bloomfilter import BloomFilter, BIP37_CONSTANT\n",
    "from helper import bit_field_to_bytes, murmur3\n",
    "\n",
    "field_size = 10\n",
    "function_count = 5\n",
    "tweak = 99\n",
    "items = (b'Hello World',  b'Goodbye!')\n",
    "# calculate the bitfield size\n",
    "# create an empty bit field\n",
    "# loop through items\n",
    "    # loop through function count\n",
    "        # calculate the seed\n",
    "        # get the murmur3 hash of the item using the seed\n",
    "        # mod by the bitfield size\n",
    "        # set the bit\n",
    "# convert the bit field to bytes\n",
    "# print the bytes in hex"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 3\n",
    "\n",
    "Write the `add` method for `BloomFilter`\n",
    "\n",
    "#### Make [this test](/edit/code-ch12/bloomfilter.py) pass: `bloomfilter.py:BloomFilterTest:test_add`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 3\n",
    "\n",
    "reload(bloomfilter)\n",
    "run(bloomfilter.BloomFilterTest(\"test_add\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 4\n",
    "\n",
    "Write the  `filterload` payload from the `BloomFilter` class.\n",
    "\n",
    "#### Make [this test](/edit/code-ch12/bloomfilter.py) pass: `bloomfilter.py:BloomFilterTest:test_filterload`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 4\n",
    "\n",
    "reload(bloomfilter)\n",
    "run(bloomfilter.BloomFilterTest(\"test_filterload\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 5\n",
    "\n",
    "Write the `serialize` method for the `GetDataMessage` class.\n",
    "\n",
    "#### Make [this test](/edit/code-ch12/network.py) pass: `network.py:GetDataMessageTest:test_serialize`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 5\n",
    "\n",
    "reload(network)\n",
    "run(network.GetDataMessageTest(\"test_serialize\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bloomfilter import BloomFilter\n",
    "from helper import decode_base58\n",
    "from merkleblock import MerkleBlock\n",
    "from network import FILTERED_BLOCK_DATA_TYPE, GetHeadersMessage, GetDataMessage, HeadersMessage, SimpleNode\n",
    "from tx import Tx\n",
    "last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55ea912e19'\n",
    "address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'\n",
    "h160 = decode_base58(address)\n",
    "node = SimpleNode('testnet.programmingbitcoin.com', testnet=True, logging=False)\n",
    "bf = BloomFilter(size=30, function_count=5, tweak=90210)\n",
    "bf.add(h160)\n",
    "node.handshake()\n",
    "node.send(bf.filterload())\n",
    "start_block = bytes.fromhex(last_block_hex)\n",
    "getheaders = GetHeadersMessage(start_block=start_block)\n",
    "node.send(getheaders)\n",
    "headers = node.wait_for(HeadersMessage)\n",
    "getdata = GetDataMessage()\n",
    "for b in headers.blocks:\n",
    "    if not b.check_pow():\n",
    "        raise RuntimeError('proof of work is invalid')\n",
    "    getdata.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash())\n",
    "node.send(getdata)\n",
    "found = False\n",
    "while not found:\n",
    "    message = node.wait_for(MerkleBlock, Tx)\n",
    "    if message.command == b'merkleblock':\n",
    "        if not message.is_valid():\n",
    "            raise RuntimeError('invalid merkle proof')\n",
    "    else:\n",
    "        for i, tx_out in enumerate(message.tx_outs):\n",
    "            if tx_out.script_pubkey.address(testnet=True) == address:\n",
    "                print('found: {}:{}'.format(message.id(), i))\n",
    "                found = True\n",
    "                break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 6\n",
    "\n",
    "Get the current testnet block ID, send yourself some testnet coins, find the UTXO corresponding to the testnet coin _without using a block explorer_, create a transaction using that UTXO as an input and broadcast the `tx` message on the  testnet network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 6\n",
    "\n",
    "import time\n",
    "\n",
    "from block import Block\n",
    "from bloomfilter import BloomFilter\n",
    "from ecc import PrivateKey\n",
    "from helper import hash256, little_endian_to_int, encode_varint, read_varint, decode_base58, SIGHASH_ALL\n",
    "from merkleblock import MerkleBlock\n",
    "from network import (\n",
    "    GetDataMessage,\n",
    "    GetHeadersMessage,\n",
    "    HeadersMessage,\n",
    "    NetworkEnvelope,\n",
    "    SimpleNode,\n",
    "    TX_DATA_TYPE,\n",
    "    FILTERED_BLOCK_DATA_TYPE,\n",
    ")\n",
    "from script import p2pkh_script, Script\n",
    "from tx import Tx, TxIn, TxOut\n",
    "\n",
    "last_block_hex = ''  # FILL THIS IN\n",
    "\n",
    "secret = little_endian_to_int(hash256(b''))  # FILL THIS IN\n",
    "private_key = PrivateKey(secret=secret)\n",
    "addr = private_key.point.address(testnet=True)\n",
    "h160 = decode_base58(addr)\n",
    "\n",
    "target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'\n",
    "target_h160 = decode_base58(target_address)\n",
    "target_script = p2pkh_script(target_h160)\n",
    "fee = 5000  # fee in satoshis\n",
    "\n",
    "\n",
    "# connect to testnet.programmingbitcoin.com in testnet mode\n",
    "# create a bloom filter of size 30 and 5 functions. Add a tweak.\n",
    "# add the h160 to the bloom filter\n",
    "# complete the handshake\n",
    "# load the bloom filter with the filterload command\n",
    "\n",
    "# set start block to last_block from above\n",
    "# send a getheaders message with the starting block\n",
    "\n",
    "# wait for the headers message\n",
    "# store the last block as None\n",
    "# initialize the GetDataMessage\n",
    "# loop through the blocks in the headers\n",
    "    # check that the proof of work on the block is valid\n",
    "    # check that this block's prev_block is the last block\n",
    "    # add a new item to the get_data_message\n",
    "    # should be FILTERED_BLOCK_DATA_TYPE and block hash\n",
    "    # set the last block to the current hash\n",
    "# send the getdata message\n",
    "\n",
    "# initialize prev_tx and prev_index to None\n",
    "# loop while prev_tx is None \n",
    "    # wait for the merkleblock or tx commands\n",
    "    # if we have the merkleblock command\n",
    "        # check that the MerkleBlock is valid\n",
    "    # else we have the tx command\n",
    "        # set the tx's testnet to be True\n",
    "        # loop through the tx outs\n",
    "            # if our output has the same address as our address we found it\n",
    "                # we found our utxo. set prev_tx, prev_index, and tx\n",
    "# create the TxIn\n",
    "# calculate the output amount (previous amount minus the fee)\n",
    "# create a new TxOut to the target script with the output amount\n",
    "# create a new transaction with the one input and one output\n",
    "# sign the only input of the transaction\n",
    "# serialize and hex to see what it looks like\n",
    "# send this signed transaction on the network\n",
    "# wait a sec so this message goes through with time.sleep(1) \n",
    "# now ask for this transaction from the other node\n",
    "# create a GetDataMessage\n",
    "# ask for our transaction by adding it to the message\n",
    "# send the message\n",
    "# now wait for a Tx response\n",
    "# if the received tx has the same id as our tx, we are done!"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 2
}
